﻿using System;
using System.Collections.Generic;
using System.Collections;
using Simp.CodeModel.CoreFunctions;

namespace Simp.CodeModel.CoreFunctions.Sequences
{
	/// <summary>Sequence deriving from a source sequence where each element is transformed by a function.</summary>
	public class DerivedSequenceFunction : IFunction, ISequenceFunction, IFunctionSerializer
	{
		#region Inner Types
		private class DerivedSequenceAccessor : ISequenceAccessor
		{
			private readonly ISequenceAccessor _sourceAccessor;
			private readonly FunctionReference _transform;

			public DerivedSequenceAccessor(ISequenceAccessor sourceAccessor, FunctionReference transform)
			{
				_sourceAccessor = sourceAccessor;
				_transform = transform;
			}

			#region ISequenceAccessor Members
			int ISequenceAccessor.Count
			{
				get { return _sourceAccessor.Count; }
			}

			IFunction ISequenceAccessor.this[int index]
			{
				get { return Transform(_sourceAccessor[index]); }
			}
			#endregion

			#region IEnumerable<IFunction> Members
			IEnumerator<IFunction> IEnumerable<IFunction>.GetEnumerator()
			{
				foreach (IFunction sourceElement in _sourceAccessor)
				{
					yield return Transform(sourceElement);
				}
			}
			#endregion

			#region IEnumerable Members
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<IFunction>)this).GetEnumerator();
			}
			#endregion

			private IFunction Transform(IFunction function)
			{
				BoundFunctionCalling boundFunction = new BoundFunctionCalling(_transform.InnerFunction, function);

				return boundFunction;
			}
		}
		#endregion

		private static readonly string[] PARAMETER_NAMES = new[] { "source", "transform" };

		/// <summary>Name of the function.</summary>
		public const string ExplicitSequenceFunctionName = "DerivedSequence";

		#region IFunction Members
		string IFunction.FunctionName
		{
			get { return ExplicitSequenceFunctionName; }
		}

		string[] IFunction.ParameterNames
		{
			get { return PARAMETER_NAMES; }
		}

		IFunctionParameterBinding[] IFunction.DefaultParameterValues
		{
			get { return FunctionHelper.EmptyBindings; }
		}

		IFunction IFunction.Simplify(SimplificationMode simplificationMode, params IFunction[] parameters)
		{
			return null;
		}

		IFunction IFunction.AcceptVisitor(IFunctionVisitor visitor)
		{
			return null;
		}
		#endregion

		#region ISequenceFunction Members
		ISequenceAccessor ISequenceFunction.GetAccessor(params IFunction[] parameters)
		{
			FunctionHelper.ValidateParameters(this, parameters);

			IFunction source = parameters[0];
			ISequenceAccessor accessor = FunctionHelper.GetAccessor(source);

			if (accessor != null)
			{
				FunctionReference transform = parameters[1] as FunctionReference;

				if (transform != null)
				{
					return new DerivedSequenceAccessor(accessor, transform);
				}
			}

			//	Can't access
			return null;
		}
		#endregion

		#region IFunctionSerializer Members
		void IFunctionSerializer.Serialize(AppendOnlyStringBuilder builder, params IFunction[] parameters)
		{
			if (builder == null)
			{
				throw new ArgumentNullException("builder");
			}
			FunctionHelper.ValidateParameters(this, parameters);

			IFunction source = parameters[0];
			IFunction transform = parameters[1];

			builder.Append("{ ");
			FunctionHelper.Serialize(source, builder, parameters);
			builder.Append(" | ");
			FunctionHelper.Serialize(transform, builder, parameters);
			builder.Append(" }");
		}
		#endregion
	}
}